package org.budo.dubbo.protocol.http.view.render;

import java.io.ByteArrayInputStream;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.budo.dubbo.protocol.http.cipher.CipherService;
import org.budo.dubbo.protocol.http.cipher.CipherUtil;
import org.budo.dubbo.protocol.http.dispatcher.servlet.ServletProtocolRequest;
import org.budo.dubbo.protocol.http.invocation.BudoDubboRpcInvocation;
import org.budo.dubbo.protocol.http.invocation.adapter.ProtocolRequest;
import org.budo.dubbo.protocol.http.invocation.adapter.ProtocolResponse;
import org.budo.dubbo.protocol.http.util.BudoSerializationUtil;
import org.budo.dubbo.serialize.util.DubboSerializeUtil;

import com.alibaba.dubbo.common.serialize.Serialization;
import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.rpc.Result;

/**
 * @author lmw
 */
public abstract class AbstractViewRender implements ViewRender {
    @Override
    public void renderView(ProtocolRequest protocolRequest, ProtocolResponse protocolResponse, Object result) throws Throwable {
        this.addHeader(protocolResponse, result);

        this.writeResponse(protocolRequest, protocolResponse, result);
    }

    protected void addHeader(ProtocolResponse response, Object result) {
        // 
    }

    protected void writeResponse(ProtocolRequest request, ProtocolResponse response, Object result) {
        this.processResultValueAndSerializeAndWriteToResponse(request, response, result);
    }

    protected void processResultValueAndSerializeAndWriteToResponse(ProtocolRequest request, ProtocolResponse response, Object result) {
        Serialization serialization = BudoSerializationUtil.getSerialization(request);

        // 对于json序列化，由于 RpcResult 不是一个标准的 Pojo，所以需要转换一下
        if (DubboSerializeUtil.isFastJsonSerialization(serialization) //
                && null != result //
                && result instanceof Result) {

            Result resultEntity = (Result) result;
            Object resultValue = resultEntity.getValue();
            Throwable exception = resultEntity.getException();
            Map<String, String> attachments = resultEntity.getAttachments();

            result = CollectionUtils.toMap("value", resultValue, //
                    "attachments", attachments, //
                    "exception", exception);
        }

        this.serializeAndWriteToResponse(request, response, result);
    }

    protected void serializeAndWriteToResponse(ProtocolRequest protocolRequest, ProtocolResponse response, Object resultValue) {
        byte[] bytes = this.serializeResultValue(protocolRequest, resultValue);

        // 非 servlet，不加密
        if (!(protocolRequest instanceof ServletProtocolRequest)) {
            response.write(new ByteArrayInputStream(bytes));
            return;
        }

        HttpServletRequest request = ((ServletProtocolRequest) protocolRequest).getRequest();
        CipherService cipherService = (CipherService) request.getAttribute(CipherService.CIPHER_SERVICE_IN_REQUEST_ATTRIBUTE_KEY);
        if (null == cipherService) {
            response.write(new ByteArrayInputStream(bytes)); // 不需要加密
            return;
        }

        // 消息加密
        BudoDubboRpcInvocation budoInvocation = (BudoDubboRpcInvocation) request.getAttribute(BudoDubboRpcInvocation.BUDO_INVOCATION_IN_REQUEST_ATTRIBUTE_KEY);
        bytes = CipherUtil.cipher(cipherService, budoInvocation, new String(bytes));

        response.write(new ByteArrayInputStream(bytes));
    }

    protected byte[] serializeResultValue(ProtocolRequest protocolRequest, Object resultValue) {
        Serialization serialization = BudoSerializationUtil.getSerialization(protocolRequest);
        return DubboSerializeUtil.serializeResultValue(serialization, resultValue, true);
    }
}